package org.chartsy.moneyflow; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.Stroke; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import org.chartsy.main.ChartFrame; import org.chartsy.main.chart.Indicator; import org.chartsy.main.data.DataItem; import org.chartsy.main.data.Dataset; import org.chartsy.main.intervals.DailyInterval; import org.chartsy.main.utils.DefaultPainter; import org.chartsy.main.utils.Range; import org.chartsy.main.utils.SerialVersion; import org.openide.nodes.AbstractNode; /** * * @author viorel.gheba */ public class MoneyFlow extends Indicator { private static final long serialVersionUID = SerialVersion.APPVERSION; public static final String MFH = "mfh"; public static final String MFL = "mfl"; private boolean toggle = false; private boolean toggle2 = false; private IndicatorProperties properties; public MoneyFlow() { super(); properties = new IndicatorProperties(); } public String getName(){ return "Money Flow"; } public String getLabel(){ return properties.getLabel(); } public Indicator newInstance(){ return new MoneyFlow(); } public String getPaintedLabel(ChartFrame cf) { if (cf.getChartData().getInterval() instanceof DailyInterval) { DecimalFormat df = new DecimalFormat("###,###"); double factor = getFactor(cf); return getLabel() + " x " + df.format((int)factor); } else { return "Money Flow indicator is available only for daily data"; } } public LinkedHashMap getHTML(ChartFrame cf, int i) { LinkedHashMap ht = new LinkedHashMap(); DecimalFormat df = new DecimalFormat("#,##0.00"); double[] values = getValues(cf, i); String[] labels = !toggle2 ? new String[] {"+MF:"} : new String[] {"-MF:"}; ht.put(getLabel(), " "); if (cf.getChartData().getInterval() instanceof DailyInterval) { if (values.length > 0) { Color[] colors = getColors(); for (int j = 0; j < values.length; j++) { ht.put(getFontHTML(colors[j], labels[j]), getFontHTML(colors[j], df.format(values[j]))); } } } return ht; } @Override public Range getRange(ChartFrame cf) { double factor = getFactor(cf); Range range = super.getRange(cf); double d = Math.max(Math.abs(range.getLowerBound()), Math.abs(range.getUpperBound())) / factor; return new Range(-1*d, d); } public void paint(Graphics2D g, ChartFrame cf, Rectangle bounds) { if (cf.getChartData().getInterval() instanceof DailyInterval) { Dataset mfh = visibleDataset(cf, MFH); Dataset mfl = visibleDataset(cf, MFL); if (mfh != null && mfl != null) { if (maximized) { double factor = getFactor(cf); Range range = getRange(cf); DefaultPainter.bar(g, cf, range, bounds, Dataset.DIV(mfh, factor), properties.getMFHColor()); DefaultPainter.bar(g, cf, range, bounds, Dataset.DIV(mfl, factor), properties.getMFLColor()); } } } } public void calculate() { Dataset initial = getDataset(); if (initial != null && !initial.isEmpty()) { int count = initial.getItemsCount(); Dataset mfh = Dataset.EMPTY(count); Dataset mfl = Dataset.EMPTY(count); mfh.setDataItem(0, new DataItem(initial.getTimeAt(0), ((initial.getCloseAt(0) + initial.getHighAt(0) + initial.getLowAt(0)) / 3) * initial.getVolumeAt(0))); for (int i = 1; i < count; i++) { double tp1 = (initial.getCloseAt(i) + initial.getHighAt(i) + initial.getLowAt(i)) / 3; double tp2 = (initial.getCloseAt(i-1) + initial.getHighAt(i-1) + initial.getLowAt(i-1)) / 3; double rmf = tp1 * initial.getVolumeAt(i); if (tp1 > tp2) mfh.setDataItem(i, new DataItem(initial.getTimeAt(i), rmf)); else if (tp1 < tp2) mfl.setDataItem(i, new DataItem(initial.getTimeAt(i), -1*rmf)); } addDataset(MFH, mfh); addDataset(MFL, mfl); } } public boolean hasZeroLine(){ return true; } public boolean getZeroLineVisibility(){ return properties.getZeroLineVisibility(); } public Color getZeroLineColor(){ return properties.getZeroLineColor(); } public Stroke getZeroLineStroke(){ return properties.getZeroLineStroke(); } public boolean hasDelimiters(){ return false; } public boolean getDelimitersVisibility(){ return false; } public double[] getDelimitersValues(){ return new double[] {}; } public Color getDelimitersColor(){ return null; } public Stroke getDelimitersStroke(){ return null; } public Color[] getColors() { if (!toggle) return new Color[] {properties.getMFHColor()}; else return new Color[] {properties.getMFLColor()}; } public double[] getValues(ChartFrame cf) { Dataset mfh = visibleDataset(cf, MFH); Dataset mfl = visibleDataset(cf, MFL); double factor = getFactor(cf); if (mfh.getDataItem(mfh.getLastIndex()) != null) { toggle = false; return new double[] {mfh.getLastClose()/factor}; } if (mfl.getDataItem(mfl.getLastIndex()) != null) { toggle = true; return new double[] {mfl.getLastClose()/factor}; } return new double[] {}; } public double[] getValues(ChartFrame cf, int i) { Dataset mfh = visibleDataset(cf, MFH); Dataset mfl = visibleDataset(cf, MFL); double factor = getFactor(cf); if (mfh.getDataItem(mfh.getLastIndex()) != null) { toggle2 = false; return new double[] {mfh.getCloseAt(i)/factor}; } if (mfl.getDataItem(mfl.getLastIndex()) != null) { toggle2 = true; return new double[] {mfl.getCloseAt(i)/factor}; } return new double[] {}; } public boolean getMarkerVisibility(){ return properties.getMarker(); } public AbstractNode getNode(){ return new IndicatorNode(properties); } @Override public Double[] getPriceValues(ChartFrame cf) { List<Double> list = new ArrayList<Double>(); Range range = getRange(cf); int step = (int) (range.getUpperBound() / 3) + 1; for (int i = step; i <= range.getUpperBound(); i+=step) { list.add(new Double(i)); list.add(new Double(-1*i)); } return list.toArray(new Double[list.size()]); } private double getFactor(ChartFrame cf) { Dataset mfh = visibleDataset(cf, MFH); Dataset mfl = visibleDataset(cf, MFL); if (mfh != null && mfl != null) { double max = mfh.getCloseAt(0); double min = mfl.getCloseAt(0); for (int i = 1; i < mfh.getItemsCount(); i++) { if (max < mfh.getCloseAt(i)) max = mfh.getCloseAt(i); if (min > mfl.getCloseAt(i)) min = mfl.getCloseAt(i); } int scaleMax = Integer.toString((int)Math.round(max + (max - min) * 0.2d)).length() - 1; int scaleMin = Integer.toString((int)Math.round(min - (max - min) * 0.2d)).length() - 1; int scale = Math.min(scaleMin, scaleMax); if (scale > 1) scale--; return Math.pow(10, scale); } return 1; } }